/*
 * Copyright (c) 2011 Seoul National University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Jung Ho Ahn  ( gajh@snu.ac.kr )
 *
 */

#include "ContigNode.h"
#include <assert.h>
#include <pthread.h>
#include <sstream>

using namespace std;


extern pthread_mutex_t read_file_m;

extern ostream & operator<<(ostream &out, nucleotide_type n);

ostream & operator<<(ostream & out, const ContigNode & k)
{
  out << k.type << k.id << ": len= " << k.length << ", hitcount= " << k.hitcount << ", ";

  if (k.prev == NULL)
  {
    out << (k.prepeat ? "[rpt] " : "NULL ");
  }
  else
  {
    out << (k.prev)->type << (k.prev)->id << " lm_hitcount= " << k.lm_hitcount << " ";
  }

  if (k.isKmerRC == true)
  {
    Kmer kmerRC(*(k.kmer));
    kmerRC.reverseComplement();
    out << kmerRC;
  }
  else
  {
    out << *(k.kmer);
  }
  out << k.nucleotides;

  if (k.next == NULL)
  {
    out << (k.nrepeat ? " [rpt]" : " NULL");
  }
  else
  {
    out << " rm_hitcount= " << k.rm_hitcount << " " << (k.next)->type << (k.next)->id;
  }

  return out;
}


ContigNode::ContigNode(KmerNode * knode, uint64_t id_)
 :Node(ndt_contig, id_), prev(NULL), next(NULL),
  kmer(knode->kmer), hitcount(knode->hitcount),
  lm_hitcount(knode->hitcount), rm_hitcount(knode->hitcount),
  length(knode->kmer->getLength()),
  prepeat(false), nrepeat(false), isKmerRC(knode->isKmerRC), nucleotides()
{
  assert(knode->has_branch == false);

  if (knode->next_type != nt_I)
  {
    next = knode->next.node;
  }
  KmerNode * currKmerNode = knode;
  Kmer     * currKmer     = knode->kmer;
  while (next != NULL && next->hasBranch() == false)
  {
    KmerNode * nextKmerNode = (KmerNode *) next;
    if (nextKmerNode->valid == false)
    {
      next = NULL;
      nrepeat = true;
      break;
    }
    nucleotides += nextKmerNode->isKmerRC ?
                   Kmer::nucleotideToChar(Kmer::ComplementaryNucleotide(nextKmerNode->kmer->getMSBNucleotide())) :
                   Kmer::nucleotideToChar(nextKmerNode->kmer->getLSBNucleotide());
    currKmer            = nextKmerNode->kmer;
    nextKmerNode->valid = false;
    rm_hitcount         = nextKmerNode->hitcount;
    hitcount           += rm_hitcount;
    currKmerNode        = nextKmerNode;
    next                = (nextKmerNode->next_type == nt_I) ? NULL : nextKmerNode->next.node;
    length++;
  }
  if (next != NULL)
  {
    KmerNode * nextKmerNode = (KmerNode *) next;
    if (currKmerNode->isKmerRC == true)
    {
      nextKmerNode->setPrev(this, Kmer::ComplementaryNucleotide(currKmer->getLSBNucleotide()));
    }
    else
    {
      nextKmerNode->setPrev(this, currKmer->getMSBNucleotide());
    }
  }

  if (knode->prev_type != nt_I)
  {
    prev = knode->prev.node;
  }
  currKmerNode = knode;
  currKmer     = knode->kmer;
  while (prev != NULL && prev->hasBranch() == false)
  {
    KmerNode * prevKmerNode = (KmerNode *) prev;
    if (prevKmerNode->valid == false)
    {
      prev = NULL;
      prepeat = true;
      break;
    }
    nucleotides = (isKmerRC ?
                   Kmer::nucleotideToChar(Kmer::ComplementaryNucleotide(kmer->getMSBNucleotide())) :
                   Kmer::nucleotideToChar(kmer->getLSBNucleotide())) + nucleotides;
    kmer = prevKmerNode->kmer;
    isKmerRC = prevKmerNode->isKmerRC;

    currKmer            = prevKmerNode->kmer;
    prevKmerNode->valid = false;
    lm_hitcount         = prevKmerNode->hitcount;
    hitcount           += lm_hitcount;
    currKmerNode        = prevKmerNode;
    prev                = (prevKmerNode->prev_type == nt_I) ? NULL : prevKmerNode->prev.node;
    length++;
  }
  if (prev != NULL)
  {
    KmerNode * prevKmerNode = (KmerNode *) prev;
    assert(prev->type != ndt_contig);
    if (currKmerNode->isKmerRC == true)
    {
      prevKmerNode->setNext(this, Kmer::ComplementaryNucleotide(currKmer->getMSBNucleotide()));
    }
    else
    {
      nucleotide_type idx = currKmer->getLSBNucleotide();
      prevKmerNode->setNext(this, idx);
    }
  }
  knode->valid = false;
}


ContigNode::ContigNode(uint64_t id_)
 :Node(ndt_contig, id_), prev(NULL), next(NULL), kmer(NULL),
  hitcount(0), length(0), prepeat(false), nrepeat(false), isKmerRC(false), nucleotides()
{
}


ContigNode::~ContigNode()
{
}


uint64_t ContigNode::fasta_id = 0;


void ContigNode::fastaDisplay(ostream & out, bool isPalindrome)
{
  ostringstream outstr;
  if (isPalindrome == true)
  {
    //out << ">" << type << id << "_len_" << length/2 << "_hitcount_" << hitcount << "_palindrom" << endl;
    out << ">" << id << fasta_id << " " << length/2 << " " << hitcount << " palindrom" << endl;
  }
  else
  {
    //out << ">" << type << id << "_len_" << length << "_hitcount_" << hitcount << endl;
    out << ">" << id << " " << fasta_id << " " << length << " " << hitcount << endl;
  }
  fasta_id++;

  if (isKmerRC == true)
  {
    Kmer kmerRC(*kmer);
    kmerRC.reverseComplement();
    outstr << kmerRC;
  }
  else
  {
    outstr << *kmer;
  }
  string   ctg = outstr.str() + nucleotides;
  assert(ctg.size() == length);
  uint64_t cnt = 0;

  if (isPalindrome == true)
  {
    ctg = ctg.substr(0, ctg.size()/2);
  }

  while (cnt < ctg.size())
  {
    out << ctg.substr(cnt, 60) << endl;
    cnt += 60;
  }
}

